/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：太平金科-基础组件封装层-登录通-缺省实现
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taipingframework.boot.dlt.filters;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taiping.dlt.application.constant.DefaultConstant;
import com.taipingframework.utility.constant.ApplicationConstant;
import com.taipingframework.utility.context.SpringContextHolder;
import com.taipingframework.utility.exception.UtilityException;
//import com.taipingframework.utility.extend.HttpsHelper;
import com.taipingframework.utility.web.properties.FluxFilterProperty;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.Ordered;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.DecoderHttpMessageReader;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.lang.NonNull;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.support.WebExchangeDataBinder;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebExchangeDecorator;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 过滤特殊字符
 */
@Slf4j
public class DltFilter implements WebFilter, Ordered {

    private FluxFilterProperty filterProperty;
    private ObjectMapper objectMapper;
    private ServerCodecConfigurer codecConfigurer;

    public DltFilter() {
        filterProperty = new FluxFilterProperty();
    }

    @NonNull
    @Override
    public Mono<Void> filter(@NonNull ServerWebExchange exchange, @NonNull WebFilterChain chain) {
        // Mono<T> defer = Mono.defer(() -> Mono.fromFuture());
        // Flux<Object> defer = Flux.defer(() -> Flux.fromIterable());
        final String requestId = exchange.getAttribute(ApplicationConstant.RESPONSE_IDENTITY);

        log.debug("进入登录通过滤器 - Web请求（{}）开始", requestId);
        /*
        return chain.filter(exchange)
                .doFinally(signalType -> {
                    log.debug("登录通过滤器 - Web请求（{}）结束", requestId);
                });
        */

        if (codecConfigurer == null) {
            codecConfigurer = SpringContextHolder.getBean(ServerCodecConfigurer.class);
        }

        if (objectMapper == null) {
            objectMapper = SpringContextHolder.getBean(ObjectMapper.class);
        }

        if (exchange.getRequest().getPath().value().startsWith(DefaultConstant.DLT_URL_PATTERN)) {
            // 在请求`登录通`的接口时，执行`登录通`过滤器的请求参数处理逻辑
            return readRequestBody(exchange)
                    .map(requestBody -> exchangeHandle(exchange, requestBody))
                    .flatMap(chain::filter)
                    .doOnTerminate(() -> log.debug("执行登录通过滤器 - Web请求（{}）结束", requestId));
        } else {
            // 如果没有请求`登录通`的接口，则不执行`登录通`过滤器的请求参数处理逻辑
            return chain.filter(exchange)
                    .doOnTerminate(() -> log.debug("跳过登录通过滤器 - Web请求（{}）结束", requestId));
        }
    }

    @Override
    public int getOrder() {
        return filterProperty.getOrderDltFilter();
    }

    @Autowired(required = false)
    public void setFilterProperty(FluxFilterProperty filterProperty) {
        this.filterProperty = filterProperty;
    }

    @Autowired(required = false)
    @Qualifier("objectMapper")
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Autowired(required = false)
    public void setCodecConfigurer(ServerCodecConfigurer codecConfigurer) {
        this.codecConfigurer = codecConfigurer;
    }

    /**
     * 读取ServerHttpRequest请求体
     */
    @SuppressWarnings("unchecked")
    private Mono<Map<String, Object>> readRequestBody(ServerWebExchange exchange) {
        MediaType contentType = exchange.getRequest().getHeaders().getContentType();
        if (Objects.nonNull(contentType) && MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
            HttpMessageReader messageReader = new DecoderHttpMessageReader<>(new Jackson2JsonDecoder(objectMapper));
            ResolvableType resolvableType = ResolvableType.forType(new ParameterizedTypeReference<Map<String, Object>>() {
            });
            return (Mono<Map<String, Object>>) messageReader.readMono(resolvableType, exchange.getRequest(), Collections.emptyMap());
        } else {
            return WebExchangeDataBinder.extractValuesToBind(exchange);
        }
    }

    /**
     * 对requestBody进行二次加工
     */
    private ServerWebExchange exchangeHandle(ServerWebExchange exchange, Map<String, Object> requestBody) throws UtilityException {
        try {
            if (requestBody.isEmpty()) {
                return exchange;
            }
            ServerHttpResponse response = new ServerHttpResponseWrapper(exchange, codecConfigurer.getReaders());

            MediaType contentType = exchange.getRequest().getHeaders().getContentType();
            if (Objects.nonNull(contentType) && MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
                Map<String, Object> targetData = requestBodyHandle(requestBody);
                byte[] rawBody = objectMapper.writeValueAsBytes(targetData);

                ServerHttpRequest request = new ServerHttpRequestWrapper(exchange, rawBody);
                return exchange.mutate().request(request).response(response).build();
            }

            ServerWebExchange exg = new ServerWebExchangeWrapper(exchange);
            return exg.mutate().response(response).build();
        } catch (Exception e) {
            throw new UtilityException(e);
        }
    }

    /**
     * 对requestBody进行二次加工，例如解密、解码、特殊字符处理等
     */
    private Map<String, Object> requestBodyHandle(Map<String, Object> requestBody) {
        // there are something to do.
        return requestBody;
//        Iterator<Map.Entry<String, Object>> iterator = requestBody.entrySet().iterator();
//        while (iterator.hasNext()) {
//            Map.Entry<String, Object> entry = iterator.next();
//            String value = HttpsHelper.decrypt(String.valueOf(entry.getValue()), "test1");
//            requestBody.put(entry.getKey(), value);
//        }
//        return requestBody;
    }

    /**
     * 对responseBody进行二次处理，例如加密、编码
     */
    private byte[] responseBodyHandle(byte[] originalBody) {
        // there are something to do.
        return originalBody;

//        String responseContent = new String(originalBody, StandardCharsets.UTF_8);
//        return HttpsHelper.encrypt(responseContent, "test1").getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 对Form-Data进行二次处理，例如解密、解码、特殊字符处理等
     */
    private MultiValueMap<String, String> formDataHandle(MultiValueMap<String, String> formData) {
        // there are something to do.
        return formData;
    }


    /**
     * 对 ServerHttpRequest 进行包装，允许重复订阅`请求体`
     */
    class ServerHttpRequestWrapper extends ServerHttpRequestDecorator {

        private final ServerWebExchange exchange;
        private final byte[] rawBody;

        /**
         * ServerHttpRequest对象构造器
         */
        ServerHttpRequestWrapper(ServerWebExchange exchange, byte[] rawBody) {
            super(exchange.getRequest());
            this.exchange = exchange;
            this.rawBody = rawBody;
        }

        @NonNull
        @Override
        public MultiValueMap<String, String> getQueryParams() throws UtilityException {
            try {
                MultiValueMap<String, String> targetMap = new LinkedMultiValueMap<>(new LinkedHashMap<>());
                targetMap.addAll(super.getQueryParams());

                Map<String, Object> sourceMap = objectMapper.readValue(this.rawBody, new TypeReference<Map<String, Object>>() {
                });
                sourceMap.forEach((k, v) -> {
                    if (Objects.nonNull(v)) {
                        targetMap.add(k, String.valueOf(v));
                    }
                });

                return targetMap;
            } catch (Exception e) {
                throw new UtilityException(e);
            }
        }

        @NonNull
        @Override
        public HttpHeaders getHeaders() {
            HttpHeaders headers = HttpHeaders.writableHttpHeaders(super.getHeaders());
            headers.setContentLength(this.rawBody.length);
            headers.setContentType(MediaType.APPLICATION_JSON);

            return headers;
        }

        @NonNull
        @Override
        public Flux<DataBuffer> getBody() {
            return Flux.just(exchange.getResponse().bufferFactory().wrap(this.rawBody));
        }

        public String bodyToString() {
            return new String(rawBody, StandardCharsets.UTF_8);
        }

    }


    /**
     * 对 ServerHttpResponse 进行包装，对响应内容统一加工处理
     */
    class ServerHttpResponseWrapper extends ServerHttpResponseDecorator {

        private final List<HttpMessageReader<?>> messageReaders;
        private String body;

        /**
         * ServerHttpResponse对象构造器
         */
        ServerHttpResponseWrapper(ServerWebExchange exchange, List<HttpMessageReader<?>> messageReaders) {
            super(exchange.getResponse());
            this.messageReaders = messageReaders;
        }

        /**
         * 重写响应内容
         */
        @NonNull
        @Override
        public Mono<Void> writeWith(@NonNull Publisher<? extends DataBuffer> body) {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);

            ClientResponse.Builder builder = ClientResponse.create(HttpStatus.OK, messageReaders);
            ClientResponse clientResponse = builder.headers(headers -> headers.putAll(httpHeaders)).body(Flux.from(body)).build();
            Mono<DataBuffer> dataBufferMono = clientResponse.bodyToMono(byte[].class)
                    .map(originalBody -> {
                        this.body = new String(originalBody, StandardCharsets.UTF_8);
                        return responseBodyHandle(originalBody); // 此处可对响应内容进一步处理，例如加密
                    })
                    .map(targetData -> super.getDelegate()
                            .bufferFactory()
                            .wrap(targetData)
                    )
                    .doOnNext(dataBuffer -> super.getDelegate()
                            .getHeaders()
                            .setContentLength(dataBuffer.readableByteCount())
                    );

            return super.writeWith(dataBufferMono);
        }

        public String bodyToString() {
            return this.body;
        }

    }


    /**
     * 对 ServerWebExchange 进行包装
     */
    class ServerWebExchangeWrapper extends ServerWebExchangeDecorator {

        ServerWebExchangeWrapper(ServerWebExchange delegate) {
            super(delegate);
        }

        @NonNull
        @Override
        public Mono<MultiValueMap<String, String>> getFormData() {
            return super.getFormData()
                    .map(DltFilter.this::formDataHandle);
        }

    }

}
